<?php
/*
* ElementalPHP 1.1.0
* Copyright (c) 2010 Chris Morrow
* Licensed under the MIT License
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/

class Element {
	protected $tag;
	public $contents;
	public $attributes;
	
	public function __construct($tag, $content = NULL, Array $attributes = NULL) {
		$this->tag = $tag;
		$this->contents = $content;
		$this->attributes = $attributes;
	}
	
	public function __toString() {
		$attributes = '';
		$c = NULL;
		if($this->attributes) {
			if(is_array($this->attributes)) {
				foreach($this->attributes as $attr => $value) {
					$attributes .= " $attr=\"$value\"";
				}
			}
			else {
				return '';
			}
		}
		if($this->contents == NULL) {
			return "<$this->tag$attributes />";
		}
		else {
			if(is_array($this->contents)) {
				foreach($this->contents as $item) {
					$c .= ($item instanceof Element) ? $item : htmlspecialchars($item);
				}
			}
			else {
				$c = ($this->contents instanceof Element) ? $this->contents : htmlspecialchars($this->contents);
			}
			return "<$this->tag$attributes>$c</$this->tag>";
		}
	}
	
	public function appendTo(Element $parent) {
		$parent->contents .= (string)$this;
		return $this;
	}
}

class Document {
	public $version = '1.0';
	public $encoding = 'UTF-8';
	public $ns;
	public $nsURI;
	public $publicID;
	public $systemID;
	public $file;
	public $contents;
	protected $root;
	protected $dom;

	public function __construct($source) {
		if(is_file($source) && file_exists($source)) {
			$this->file = $source;
		}
		else {
			$this->contents = $source;
		}
	}

	protected function output() {
		$this->dom->formatOutput = TRUE;
		echo $this->dom->saveXML();
		return true;
	}

	protected function save() {
		$this->dom->formatOutput = TRUE;
		$this->dom->save();
		return true;
	}
	
	protected function generate() {
		$imp = new DOMImplementation();
		$docType = $imp->createDocumentType($this->ns, $this->publicID, $this->systemID);
		$this->dom = $imp->createDocument($this->nsURI, $this->ns, $docType);
		$this->dom->encoding = $this->encoding;
	}
	
	protected function import() {
		$this->dom->loadXML($this->contents);
		return true;
	}
	
	protected function importFile() {
		if(!file_exists($this->file)) {
			return false;
		}
		$this->dom = new DOMDocument();
		$this->dom->preserveWhiteSpace = FALSE;
		$this->dom->load(realpath($this->file));
		return true;
	}
}

class HTML extends Document {
	const LOCAL_NAME = 'html';
	const NSPACE = 'http://www.w3.org/1999/xhtml';

	public $title;
	public $target;
	public $inlineStyle;
	public $inlineScript;
	public $styleSheets;
	public $scripts;
	public $meta;
	
	public function build() {
		$this->ns = self::LOCAL_NAME;
		$this->nsURI = self::NSPACE;
		if($this->file) {
			$this->importFile();
		}
		else {
			$this->generate();
		}
		$xpath = new DOMXPath($this->dom);
		if($this->file) {
			if($this->dom->documentElement->tagName != $this->ns) {
				exit("$this->file is not an html file!");
			}
			$xpath->registerNameSpace($this->ns, $this->nsURI);
			$head = $xpath->query("//".$this->ns.":head")->item(0);
			$target = $xpath->query("//".$this->ns.":div[@id='".$this->target."']/.")->item(0);
			$xpath->query("//".$this->ns.":title")->item(0)->nodeValue = $this->title;
			if($this->meta) {
				$this->addMeta();
			}
			if($this->styleSheets || $this->inlineStyle) {
				$this->addStyle();
			}
			if($this->scripts || $this->inlineScript) {
				$this->addScript();
			}
		}
		else {
			$target = $xpath->query("//div[@id='".$this->target."']")->item(0);
		}
		if(!($target instanceof DOMNode)) {
			exit('target not found!');
		}
		$els = func_get_args();
		foreach($els as $e) {
			$fragment = $this->dom->createDocumentFragment();
			$fragment->appendXML($e);
			$target->appendChild($fragment);
		}
		$this->output();
		return true;
	}
	
	protected function addStyle() {
		$head = $this->dom->getElementsByTagName('head')->item(0);
		if(is_array($this->styleSheets)) {
			foreach($this->styleSheets as $css) {
				$style = $this->dom->createElement('link');
				$style->setAttribute('rel', 'stylesheet');
				$style->setAttribute('href', $css);
				$style->setAttribute('type', 'text/css');
				$head->appendChild($style);
			}
		}
		if($this->inlineStyle) {
			$style = $this->dom->createElement('style', $this->inlineStyle);
			$style->setAttribute('type', 'text/css');
			$head->appendChild($style);
		}
		return true;
	}
	
	protected function addScript() {
		$head = $this->dom->getElementsByTagName('head')->item(0);
		if(is_array($this->scripts)) {
			foreach($this->scripts as $s) {
				$script = $this->dom->createElement('script', '');
				$script->setAttribute('type', 'text/javascript');
				$script->setAttribute('src', $s);
				$head->appendChild($script);
			}
		}
		if($this->inlineScript) {
			$script = $this->dom->createElement('script', $this->inlineScript);
			$script->setAttribute('type', 'text/javascript');
			$head->appendChild($script);
		}
		return true;
	}
	
	protected function addMeta() {
		$head = $this->dom->getElementsByTagName('head')->item(0);
		if(is_array($this->meta)) {
			foreach($this->meta as $metaTag) {
				$fragment = $this->dom->createDocumentFragment();
				$fragment->appendXML($metaTag);
				$head->appendChild($fragment);
			}
			return true;
		}
		return false;
	}				
	
	protected function generate() {
		$imp = new DOMImplementation();
		$docType = $imp->createDocumentType($this->ns, $this->publicID, $this->systemID);
		$this->dom = $imp->createDocument($this->nsURI, $this->ns, $docType);
		$this->dom->xmlVersion = $this->version;
		$this->dom->encoding = $this->encoding;
		$this->dom->xmlStandalone = false;
		$head = $this->dom->createElement('head');
		$this->dom->documentElement->appendChild($head);
		$title = $this->dom->createElement('title', $this->title);
		$head->appendChild($title);
		if($this->meta) {
			$this->addMeta();
		}
		if($this->styleSheets || $this->inlineStyle) {
			$this->addStyle();
		}
		if($this->scripts || $this->inlineScript) {
			$this->addScript();
		}
		$body = $this->dom->createElement('body');
		$this->dom->documentElement->appendChild($body);
		$fragment = $this->dom->createDocumentFragment();
		$fragment->appendXML($this->contents);
		$body->appendChild($fragment);
	}
}

?>